package tools;

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import java.text.*;

/**
 * Present a JTextComponent with line and character count statistics.
 *
 * @author Keith Shie
 */
public class ResourceEditorPane extends JPanel {

  /**
   * Contains text being editted.
   */
  private JTextComponent editorText;

  /**
   * Contains the number of characters that are currently present contained
   * within <b>editorText</b>
   */
  private JLabel charCountLabel = new JLabel();

  /**
   * Contains the number of lines that are currently present contained
   * within <b>editorText</b>
   */
  private JLabel lineCountLabel = new JLabel();

  /**
   * Formatter for displaying line and character counts.
   */
  private NumberFormat fmt;

  /**
   * Default constructor.  Create using {@link JTextArea JTextArea} as the
   * editor text component.
   *
   * @see #ResourceEditorPanel(JTextComponent)
   */
  public ResourceEditorPane() {
    this( new JTextArea() );
  }

  /**
   * Construct ResourceEditorPanel with the specified JTextComponent as the
   * editor text component.
   *
   * @param tc JTextComponent to us as the editor text component.
   */
  public ResourceEditorPane( JTextComponent tc ) {
    fmt = NumberFormat.getNumberInstance();
    fmt.setGroupingUsed( true );

    editorText = tc;

    JPanel southPanel = new JPanel();
    JScrollPane editorScrollPane = new JScrollPane();
    JPanel editorStatsPanel = new JPanel();
    GridLayout gridLayout1 = new GridLayout();
    JLabel lineLabel = new JLabel( "Lines:", SwingConstants.RIGHT );
    JLabel charLabel = new JLabel( "Characters:", SwingConstants.RIGHT );

    setLayout( new BorderLayout() );

    editorText.setAutoscrolls( true );
    editorText.getDocument().addDocumentListener( new ResourceDocumentListener() );
    editorScrollPane.getViewport().add( editorText, null );
    add( editorScrollPane, BorderLayout.CENTER );

    gridLayout1.setHgap( 3 );
    editorStatsPanel.setLayout( gridLayout1 );
    editorStatsPanel.add( lineLabel );
    editorStatsPanel.add( lineCountLabel );
    editorStatsPanel.add( charLabel );
    editorStatsPanel.add( charCountLabel );
    southPanel.add( editorStatsPanel );
    add( southPanel, BorderLayout.SOUTH );
  }

  /**
   * Retrieve the editor text component.
   *
   * @return The editor text component.
   */
  public JTextComponent getTextComponent(){
    return editorText;
  }

  /**
   * Retrieve the number of lines contained with in the specified document.
   *
   * @param d The {@link Document Document} for which a line count is desired.
   * @return The number of lines with in the document.
   */
  static private int getLineCount( Document d ) {
    Element map = d.getDefaultRootElement();
    int n = map.getElementCount();
    Element lastLine = map.getElement( n - 1 );
    if ( ( lastLine.getEndOffset() - lastLine.getStartOffset() ) <= 1 ) {
      n--;
    }
    return n;
  }

  /**
   * Update the document line and character counts.
   *
   * @param d The {@link Document Document} for which the counts are desired.
   */
  protected void updateDocumentStats( Document d ){
    lineCountLabel.setText( fmt.format( getLineCount( d ) ) );
    charCountLabel.setText( fmt.format( d.getLength() ) );
  }

  /**
   * Remove any highlighted regions from the editor text component.
   */
  protected void clearHighlights(){
    Highlighter h = editorText.getHighlighter();

    if ( h != null ){
      h.removeAllHighlights();
    }
  }

  /**
   * DocumentListener which updates the document stats and clears any highlights
   * with each reported change to the document.
   *
   * @author Keith Shie
   */
  private class ResourceDocumentListener implements DocumentListener {

    public void insertUpdate(DocumentEvent e){
      updateDocumentStats( e.getDocument() );
      clearHighlights();
    }

    public void removeUpdate(DocumentEvent e){
      updateDocumentStats( e.getDocument() );
      clearHighlights();
    }

    public void changedUpdate(DocumentEvent e){
      clearHighlights();
    }
  }
}